{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/workshops/Alaska_2024_Part3.ipynb)\n",
    "\n",
    "**Geospatial Cloud Computing with the GEE Python API - Part 3**\n",
    "\n",
    "-   Notebook: <https://geemap.org/workshops/Alaska_2024_Part3>\n",
    "-   Earth Engine: <https://earthengine.google.com>\n",
    "-   Geemap: <https://geemap.org>\n",
    "\n",
    "## Introduction\n",
    "\n",
    "This notebook contains the materials for the third part of the workshop **Geospatial Cloud Computing with the GEE Python API** at the University of Alaska Fairbanks.\n",
    "\n",
    "This workshop provides an introduction to cloud-based geospatial analysis using the Earth Engine Python API. Attendees will learn the basics of Earth Engine data types and how to visualize, analyze, and export Earth Engine data in a Jupyter environment with geemap. In addition, attendees will learn how to develop and deploy interactive Earth Engine web apps with Python. Through practical examples and hands-on exercises, attendees will enhance their learning experience. During each hands-on session, attendees will walk through Jupyter Notebook examples on Google Colab with the instructors. At the end of each session, they will complete a hands-on exercise to apply the knowledge they have learned.\n",
    "\n",
    "### Agenda\n",
    "\n",
    "The workshop is divided into three parts. The third part will cover the following topics:\n",
    "\n",
    "-   Image Classification (focused on land cover in Alaska)\n",
    "-   Accuracy assessment\n",
    "-   Create and export maps\n",
    "-   Building interactive web apps\n",
    "\n",
    "### Prerequisites\n",
    "\n",
    "-   To use geemap and the Earth Engine Python API, you must [register](https://code.earthengine.google.com/register) for an Earth Engine account and follow the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing) to create a Cloud Project. Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). To test whether you can use authenticate the Earth Engine Python API, please run [this notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) on Google Colab.\n",
    "\n",
    "## Technical requirements\n",
    "\n",
    "### Install packages\n",
    "\n",
    "```bash\n",
    "conda create -n gee python=3.11\n",
    "conda activate gee\n",
    "conda install -c conda-forge mamba\n",
    "mamba install -c conda-forge pygis cartopy solara\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# %pip install geemap pygis cartopy solara"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "### Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_initialize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {},
   "source": [
    "## Image Classification\n",
    "\n",
    "### North American Land Change Monitoring System (NALCMS)\n",
    "\n",
    "The [2020 North American Land Cover 30-meter dataset](https://developers.google.com/earth-engine/datasets/catalog/USGS_NLCD_RELEASES_2020_REL_NALCMS) was produced as part of the North American Land Change Monitoring System (NALCMS), a trilateral effort between Natural Resources Canada, the United States Geological Survey, and three Mexican organizations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = geemap.Map(center=[40, -100], zoom=4, height=700)\n",
    "m.add_basemap(\"Esri.WorldImagery\", False)\n",
    "landcover = ee.Image(\"USGS/NLCD_RELEASES/2020_REL/NALCMS\")\n",
    "\n",
    "states = ee.FeatureCollection(\"TIGER/2018/States\")\n",
    "fc = states.filter(ee.Filter.eq(\"NAME\", \"Alaska\"))\n",
    "\n",
    "legend_dict = {\n",
    "    \"1 Temperate forest\": \"033e00\",\n",
    "    \"2 Sub-polar forest\": \"939b71\",\n",
    "    \"3 Tropical evergreen forest\": \"196d12\",\n",
    "    \"4 Tropical deciduous forest\": \"1fab01\",\n",
    "    \"5 Temperate deciduous forest\": \"5b725c\",\n",
    "    \"6 Mixed forest\": \"6b7d2c\",\n",
    "    \"7 Tropical shrubland\": \"b29d29\",\n",
    "    \"8 Temperate shrubland\": \"b48833\",\n",
    "    \"9 Tropical grassland\": \"e9da5d\",\n",
    "    \"10 Temperate grassland\": \"e0cd88\",\n",
    "    \"11 Sub-polar shrubland\": \"a07451\",\n",
    "    \"12 Sub-polar grassland\": \"bad292\",\n",
    "    \"13 Sub-polar barren\": \"3f8970\",\n",
    "    \"14 Wetland\": \"6ca289\",\n",
    "    \"15 Cropland\": \"e6ad6a\",\n",
    "    \"16 Barren land\": \"a9abae\",\n",
    "    \"17 Urban and built-up\": \"db2126\",\n",
    "    \"18 Water\": \"4c73a1\",\n",
    "    \"19 Snow and ice \": \"fff7fe\",\n",
    "}\n",
    "\n",
    "palette = list(legend_dict.values())\n",
    "vis_params = {\"palette\": palette, \"min\": 1, \"max\": 19}\n",
    "\n",
    "m.add_layer(landcover, vis_params, \"NALCMS Land Cover\")\n",
    "m.add_layer(fc, {}, \"Alaska\", False)\n",
    "m.center_object(fc, 4)\n",
    "m.add_legend(title=\"Land Cover Type\", legend_dict=legend_dict)\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = geemap.image_histogram(\n",
    "    landcover, region=fc, x_label=\"Land cover type\", y_label=\"Pixels\"\n",
    ")\n",
    "fig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "values = list(fig.data[0][\"x\"])\n",
    "values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9",
   "metadata": {},
   "outputs": [],
   "source": [
    "classes = [list(legend_dict.keys())[int(value) - 1] for value in values]\n",
    "classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig.update_xaxes(tickvals=values, ticktext=classes)\n",
    "fig"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11",
   "metadata": {},
   "source": [
    "### Unsupervised classification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "region = ee.Geometry.BBox(-149.352, 64.5532, -147.0976, 65.1277)\n",
    "collection = geemap.landsat_timeseries(\n",
    "    region, start_year=2021, end_year=2021, start_date=\"06-01\", end_date=\"09-01\"\n",
    ")\n",
    "image = collection.first()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = geemap.Map()\n",
    "m.add_basemap(\"Esri.WorldImagery\")\n",
    "vis_params = {\"min\": 0, \"max\": 0.3, \"bands\": [\"NIR\", \"Red\", \"Green\"]}\n",
    "m.add_layer(image, vis_params, \"Image\")\n",
    "m.add_layer(landcover.clip(region), {}, \"NALCMS land cover\", False)\n",
    "# m.add_legend(title='Land Cover Type', legend_dict=legend_dict, layer_name='NALCMS land cover')\n",
    "m.center_object(region, 9)\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "training = image.sample(\n",
    "    **{\n",
    "        \"region\": region,\n",
    "        \"scale\": 150,\n",
    "        \"numPixels\": 5000,\n",
    "        \"seed\": 1,\n",
    "        \"geometries\": True,\n",
    "    }\n",
    ")\n",
    "m.add_layer(training, {}, \"Training samples\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_to_df(training.limit(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "clusterer = ee.Clusterer.wekaXMeans(minClusters=3, maxClusters=6).train(training)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = image.cluster(clusterer)\n",
    "m.add_layer(result.randomVisualizer(), {}, \"Clusters\")\n",
    "m.add(\"layer_manager\")\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.image_histogram(landcover, region=region, scale=30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.image_histogram(result, region=region, scale=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "### Supervised classification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = geemap.Map()\n",
    "m.add_basemap(\"Esri.WorldImagery\")\n",
    "vis_params = {\"min\": 0, \"max\": 0.3, \"bands\": [\"NIR\", \"Red\", \"Green\"]}\n",
    "m.add_layer(image, vis_params, \"Image\")\n",
    "m.add_layer(landcover.clip(region), {}, \"NALCMS land cover\", False)\n",
    "# m.add_legend(title='Land Cover Type', legend_dict=legend_dict, layer_name='NALCMS land cover')\n",
    "m.center_object(region, 9)\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22",
   "metadata": {},
   "outputs": [],
   "source": [
    "points = landcover.sample(\n",
    "    **{\n",
    "        \"region\": region,\n",
    "        \"scale\": 150,\n",
    "        \"numPixels\": 5000,\n",
    "        \"seed\": 1,\n",
    "        \"geometries\": True,\n",
    "    }\n",
    ")\n",
    "\n",
    "m.add_layer(points, {}, \"Training\", False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23",
   "metadata": {},
   "outputs": [],
   "source": [
    "label = \"landcover\"\n",
    "features = image.sampleRegions(\n",
    "    **{\"collection\": points, \"properties\": [label], \"scale\": 150}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_to_df(features.limit(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {},
   "outputs": [],
   "source": [
    "bands = image.bandNames().getInfo()\n",
    "params = {\n",
    "    \"features\": features,\n",
    "    \"classProperty\": label,\n",
    "    \"inputProperties\": bands,\n",
    "}\n",
    "classifier = ee.Classifier.smileCart(maxNodes=None).train(**params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26",
   "metadata": {},
   "outputs": [],
   "source": [
    "classified = image.classify(classifier).rename(\"landcover\")\n",
    "m.add_layer(classified.randomVisualizer(), {}, \"Classified\")\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27",
   "metadata": {},
   "outputs": [],
   "source": [
    "class_values = list(range(1, 20))\n",
    "class_palette = list(legend_dict.values())\n",
    "classified = classified.set(\n",
    "    {\"landcover_class_values\": class_values, \"landcover_class_palette\": class_palette}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "m.add_layer(classified, {}, \"Land cover\")\n",
    "m.add_legend(title=\"Land cover type\", builtin_legend=\"NLCD\")\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29",
   "metadata": {},
   "source": [
    "## Accuracy assessment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = geemap.Map()\n",
    "point = ee.Geometry.Point([-122.4439, 37.7538])\n",
    "\n",
    "img = (\n",
    "    ee.ImageCollection(\"COPERNICUS/S2_SR\")\n",
    "    .filterBounds(point)\n",
    "    .filterDate(\"2020-01-01\", \"2021-01-01\")\n",
    "    .sort(\"CLOUDY_PIXEL_PERCENTAGE\")\n",
    "    .first()\n",
    "    .select(\"B.*\")\n",
    ")\n",
    "\n",
    "vis_params = {\"min\": 100, \"max\": 3500, \"bands\": [\"B11\", \"B8\", \"B3\"]}\n",
    "\n",
    "m.centerObject(point, 9)\n",
    "m.add_layer(img, vis_params, \"Sentinel-2\")\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "31",
   "metadata": {},
   "outputs": [],
   "source": [
    "lc = ee.Image(\"ESA/WorldCover/v100/2020\")\n",
    "classValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100]\n",
    "remapValues = ee.List.sequence(0, 10)\n",
    "label = \"lc\"\n",
    "lc = lc.remap(classValues, remapValues).rename(label).toByte()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32",
   "metadata": {},
   "outputs": [],
   "source": [
    "sample = img.addBands(lc).stratifiedSample(\n",
    "    **{\n",
    "        \"numPoints\": 100,\n",
    "        \"classBand\": label,\n",
    "        \"region\": img.geometry(),\n",
    "        \"scale\": 10,\n",
    "        \"geometries\": True,\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33",
   "metadata": {},
   "outputs": [],
   "source": [
    "sample = sample.randomColumn()\n",
    "trainingSample = sample.filter(\"random <= 0.8\")\n",
    "validationSample = sample.filter(\"random > 0.8\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainedClassifier = ee.Classifier.smileRandomForest(numberOfTrees=10).train(\n",
    "    **{\n",
    "        \"features\": trainingSample,\n",
    "        \"classProperty\": label,\n",
    "        \"inputProperties\": img.bandNames(),\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35",
   "metadata": {},
   "outputs": [],
   "source": [
    "# print('Results of trained classifier', trainedClassifier.explain().getInfo())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainAccuracy = trainedClassifier.confusionMatrix()\n",
    "trainAccuracy.getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainAccuracy.accuracy().getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainAccuracy.kappa().getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39",
   "metadata": {},
   "outputs": [],
   "source": [
    "validationSample = validationSample.classify(trainedClassifier)\n",
    "validationAccuracy = validationSample.errorMatrix(label, \"classification\")\n",
    "validationAccuracy.getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40",
   "metadata": {},
   "outputs": [],
   "source": [
    "validationAccuracy.accuracy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41",
   "metadata": {},
   "outputs": [],
   "source": [
    "validationAccuracy.producersAccuracy().getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42",
   "metadata": {},
   "outputs": [],
   "source": [
    "validationAccuracy.consumersAccuracy().getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43",
   "metadata": {},
   "outputs": [],
   "source": [
    "import csv\n",
    "\n",
    "with open(\"training.csv\", \"w\", newline=\"\") as f:\n",
    "    writer = csv.writer(f)\n",
    "    writer.writerows(trainAccuracy.getInfo())\n",
    "\n",
    "with open(\"validation.csv\", \"w\", newline=\"\") as f:\n",
    "    writer = csv.writer(f)\n",
    "    writer.writerows(validationAccuracy.getInfo())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44",
   "metadata": {},
   "outputs": [],
   "source": [
    "imgClassified = img.classify(trainedClassifier)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45",
   "metadata": {},
   "outputs": [],
   "source": [
    "classVis = {\n",
    "    \"min\": 0,\n",
    "    \"max\": 10,\n",
    "    \"palette\": [\n",
    "        \"006400\",\n",
    "        \"ffbb22\",\n",
    "        \"ffff4c\",\n",
    "        \"f096ff\",\n",
    "        \"fa0000\",\n",
    "        \"b4b4b4\",\n",
    "        \"f0f0f0\",\n",
    "        \"0064c8\",\n",
    "        \"0096a0\",\n",
    "        \"00cf75\",\n",
    "        \"fae6a0\",\n",
    "    ],\n",
    "}\n",
    "m.add_layer(lc, classVis, \"ESA Land Cover\", False)\n",
    "m.add_layer(imgClassified, classVis, \"Classified\")\n",
    "m.add_layer(trainingSample, {\"color\": \"black\"}, \"Training sample\")\n",
    "m.add_layer(validationSample, {\"color\": \"white\"}, \"Validation sample\")\n",
    "m.add_legend(title=\"Land Cover Type\", builtin_legend=\"ESA_WorldCover\")\n",
    "m.centerObject(img)\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46",
   "metadata": {},
   "source": [
    "## Exercise 1 - Unsupervised classification\n",
    "\n",
    "Perform an unsupervised classification of a Sentinel-2 imagery for your preferred area. Relevant Earth Engine assets:\n",
    "\n",
    "-   [ee.ImageCollection(\"COPERNICUS/S2_HARMONIZED\")](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_HARMONIZED)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "48",
   "metadata": {},
   "source": [
    "## Create and export maps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = geemap.Map(center=(41.0462, -109.7424), zoom=6)\n",
    "\n",
    "dem = ee.Image(\"USGS/SRTMGL1_003\")\n",
    "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").select(\n",
    "    [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B7\"]\n",
    ")\n",
    "\n",
    "vis_params = {\n",
    "    \"min\": 0,\n",
    "    \"max\": 4000,\n",
    "    \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n",
    "}\n",
    "\n",
    "m.add_layer(\n",
    "    landsat7,\n",
    "    {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2},\n",
    "    \"landsat\",\n",
    ")\n",
    "m.add_layer(dem, vis_params, \"dem\", True, 1)\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    m.layer_to_image(\"dem\", output=\"dem.tif\", crs=\"EPSG:3857\", region=None, scale=None)\n",
    "    m.layer_to_image(\"dem\", output=\"dem.jpg\", scale=500)\n",
    "    geemap.show_image(\"dem.jpg\")\n",
    "except:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    m.layer_to_image(\"landsat\", output=\"landsat.tif\")\n",
    "    geemap.geotiff_to_image(\"landsat.tif\", output=\"landsat.jpg\")\n",
    "    geemap.show_image(\"landsat.jpg\")\n",
    "except:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52",
   "metadata": {},
   "outputs": [],
   "source": [
    "from geemap import cartoee\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53",
   "metadata": {},
   "source": [
    "### Plotting single-band images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from geemap import cartoee"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55",
   "metadata": {},
   "outputs": [],
   "source": [
    "srtm = ee.Image(\"CGIAR/SRTM90_V4\")\n",
    "\n",
    "# define bounding box [east, south, west, north] to request data\n",
    "region = [180, -60, -180, 85]\n",
    "vis = {\"min\": 0, \"max\": 3000}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "56",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 9))\n",
    "\n",
    "# use cartoee to get a map\n",
    "ax = cartoee.get_map(srtm, region=region, vis_params=vis)\n",
    "\n",
    "# add a color bar to the map using the visualization params we passed to the map\n",
    "cartoee.add_colorbar(\n",
    "    ax, vis, loc=\"bottom\", label=\"Elevation (m)\", orientation=\"horizontal\"\n",
    ")\n",
    "\n",
    "# add grid lines to the map at a specified interval\n",
    "cartoee.add_gridlines(ax, interval=[60, 30], linestyle=\":\")\n",
    "\n",
    "# add coastlines using the cartopy api\n",
    "ax.coastlines(color=\"red\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 7))\n",
    "\n",
    "cmap = \"terrain\"\n",
    "\n",
    "ax = cartoee.get_map(srtm, region=region, vis_params=vis, cmap=cmap)\n",
    "cartoee.add_colorbar(\n",
    "    ax, vis, cmap=cmap, loc=\"right\", label=\"Elevation (m)\", orientation=\"vertical\"\n",
    ")\n",
    "\n",
    "cartoee.add_gridlines(ax, interval=[60, 30], linestyle=\"--\")\n",
    "ax.coastlines(color=\"red\")\n",
    "ax.set_title(label=\"Global Elevation Map\", fontsize=15)\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58",
   "metadata": {},
   "outputs": [],
   "source": [
    "cartoee.savefig(fig, fname=\"srtm.jpg\", dpi=300, bbox_inches=\"tight\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59",
   "metadata": {},
   "source": [
    "### Plotting multi-band images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60",
   "metadata": {},
   "outputs": [],
   "source": [
    "image = ee.Image(\"LANDSAT/LC08/C01/T1_SR/LC08_044034_20140318\")\n",
    "vis = {\"bands\": [\"B5\", \"B4\", \"B3\"], \"min\": 0, \"max\": 5000, \"gamma\": 1.3}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "ax = cartoee.get_map(image, vis_params=vis)\n",
    "cartoee.pad_view(ax)\n",
    "cartoee.add_gridlines(ax, interval=0.5, xtick_rotation=0, linestyle=\":\")\n",
    "ax.coastlines(color=\"yellow\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "region = [-121.8025, 37.3458, -122.6265, 37.9178]\n",
    "ax = cartoee.get_map(image, vis_params=vis, region=region)\n",
    "cartoee.add_gridlines(ax, interval=0.15, xtick_rotation=0, linestyle=\":\")\n",
    "ax.coastlines(color=\"yellow\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63",
   "metadata": {},
   "source": [
    "### Using custom projections\n",
    "\n",
    "#### The PlateCarree projection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "64",
   "metadata": {},
   "outputs": [],
   "source": [
    "ocean = (\n",
    "    ee.ImageCollection(\"NASA/OCEANDATA/MODIS-Terra/L3SMI\")\n",
    "    .filter(ee.Filter.date(\"2018-01-01\", \"2018-03-01\"))\n",
    "    .median()\n",
    "    .select([\"sst\"], [\"SST\"])\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65",
   "metadata": {},
   "outputs": [],
   "source": [
    "visualization = {\"bands\": \"SST\", \"min\": -2, \"max\": 30}\n",
    "bbox = [180, -88, -180, 88]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "ax = cartoee.get_map(ocean, cmap=\"plasma\", vis_params=visualization, region=bbox)\n",
    "cb = cartoee.add_colorbar(ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\")\n",
    "\n",
    "ax.set_title(label=\"Sea Surface Temperature\", fontsize=15)\n",
    "\n",
    "ax.coastlines()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67",
   "metadata": {},
   "outputs": [],
   "source": [
    "cartoee.savefig(fig, \"SST.jpg\", dpi=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68",
   "metadata": {},
   "source": [
    "#### Custom projections"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cartopy.crs as ccrs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "projection = ccrs.Mollweide(central_longitude=-180)\n",
    "ax = cartoee.get_map(\n",
    "    ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n",
    ")\n",
    "cb = cartoee.add_colorbar(\n",
    "    ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n",
    ")\n",
    "ax.set_title(\"Mollweide projection\")\n",
    "ax.coastlines()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "projection = ccrs.Robinson(central_longitude=-180)\n",
    "ax = cartoee.get_map(\n",
    "    ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n",
    ")\n",
    "cb = cartoee.add_colorbar(\n",
    "    ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n",
    ")\n",
    "ax.set_title(\"Robinson projection\")\n",
    "ax.coastlines()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "projection = ccrs.InterruptedGoodeHomolosine(central_longitude=-180)\n",
    "ax = cartoee.get_map(\n",
    "    ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n",
    ")\n",
    "cb = cartoee.add_colorbar(\n",
    "    ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n",
    ")\n",
    "ax.set_title(\"Goode homolosine projection\")\n",
    "ax.coastlines()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(15, 10))\n",
    "\n",
    "projection = ccrs.EqualEarth(central_longitude=-180)\n",
    "ax = cartoee.get_map(\n",
    "    ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n",
    ")\n",
    "cb = cartoee.add_colorbar(\n",
    "    ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\", orientation=\"vertical\"\n",
    ")\n",
    "ax.set_title(\"Equal Earth projection\")\n",
    "ax.coastlines()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(11, 10))\n",
    "\n",
    "projection = ccrs.Orthographic(-130, -10)\n",
    "ax = cartoee.get_map(\n",
    "    ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n",
    ")\n",
    "cb = cartoee.add_colorbar(\n",
    "    ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\", orientation=\"vertical\"\n",
    ")\n",
    "ax.set_title(\"Orographic projection\")\n",
    "ax.coastlines()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75",
   "metadata": {},
   "source": [
    "## Exercise 2 - Creating NDVI maps\n",
    "\n",
    "Create and export a global NDVI map using MODIS data. Relevant Earth Engine assets:\n",
    "\n",
    "- [ee.ImageCollection(\"MODIS/MCD43A4_006_NDVI\")](https://developers.google.com/earth-engine/datasets/catalog/MODIS_MCD43A4_006_NDVI)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "77",
   "metadata": {},
   "source": [
    "## Building interactive web apps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap\n",
    "import solara\n",
    "\n",
    "\n",
    "class Map(geemap.Map):\n",
    "    def __init__(self, **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.add_ee_data()\n",
    "\n",
    "    def add_ee_data(self):\n",
    "        years = [\"2001\", \"2004\", \"2006\", \"2008\", \"2011\", \"2013\", \"2016\", \"2019\"]\n",
    "\n",
    "        def getNLCD(year):\n",
    "            dataset = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\")\n",
    "            nlcd = dataset.filter(ee.Filter.eq(\"system:index\", year)).first()\n",
    "            landcover = nlcd.select(\"landcover\")\n",
    "            return landcover\n",
    "\n",
    "        collection = ee.ImageCollection(ee.List(years).map(lambda year: getNLCD(year)))\n",
    "        labels = [f\"NLCD {year}\" for year in years]\n",
    "        self.ts_inspector(\n",
    "            left_ts=collection,\n",
    "            right_ts=collection,\n",
    "            left_names=labels,\n",
    "            right_names=labels,\n",
    "        )\n",
    "        self.add_legend(\n",
    "            title=\"NLCD Land Cover Type\",\n",
    "            builtin_legend=\"NLCD\",\n",
    "            height=\"460px\",\n",
    "            add_header=False,\n",
    "        )\n",
    "\n",
    "\n",
    "@solara.component\n",
    "def Page():\n",
    "    with solara.Column(style={\"min-width\": \"500px\"}):\n",
    "        Map.element(\n",
    "            center=[40, -100],\n",
    "            zoom=4,\n",
    "            height=\"800px\",\n",
    "        )\n",
    "\n",
    "\n",
    "Page()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79",
   "metadata": {},
   "source": [
    "```bash\n",
    "solara run ./pages\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80",
   "metadata": {},
   "outputs": [],
   "source": [
    "# geemap.get_ee_token()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81",
   "metadata": {},
   "source": [
    "## Exercise 3 - Deploying an Earth Engine app on Hugging Face.\n",
    "\n",
    "Follow the instructions [here](https://huggingface.co/spaces/giswqs/solara-geemap) to deploy an Earth Engine web app on Hugging Face."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
